/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package no.ffi.tpm.aik;

import iaik.tc.apps.jtt.aik.Constants;
import iaik.tc.tss.api.constants.tpm.TcTpmConstants;
import iaik.tc.tss.api.structs.common.TcBlobData;
import iaik.tc.tss.api.structs.tpm.TcTpmCompositeHash;
import iaik.tc.tss.api.structs.tpm.TcTpmQuoteInfo2;
import iaik.tc.tss.api.structs.tpm.TcTpmSymmetricKey;
import iaik.tc.tss.impl.csp.TcCrypto;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.trustedcomputing.tpm.structures.PCRInfo;
import javax.trustedcomputing.tpm.structures.ValidationData;
import no.ffi.tpm.TPMUtils;

/**
 * Takes the decrypted AIK request from the TPM and parse it into a Java Object
 * @author Federico Mancinie <fma at ffi.no>
 */
public class PCAReqParser {
    
    /**
     * The main class that parses the request and creates the AIKRequest object
     * @param clearTextTPMReq The request from the TPM decrypted directly on the client 
     * @return A AIKRequest object
     */
     public static AIKRequest createAIKRequest(byte[] clearTextTPMReq,RSAPublicKey PCAPubKey) {
        
         
         //Create an emtpy AIKRequest object to fill as valued are parsed 
        AIKRequest req=new AIKRequest();
        
        //The first part of the request contains standard fixed size fields and 
        //the sizes of the fields containing variable length information
        int idx=0;
        //The first 4 byte contain the version of the TPM
        req.setVersion(TPMUtils.getRange(clearTextTPMReq, 0, 4));
        idx=idx+4;
        //So the size of the PCALabel, i.e., the name of the PCA 
        //that will perform the certification
        long labelSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx, TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //The size of the IdBinding structure
        long identityBindingSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx, TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //The size of the EC
        long endorsementSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx, TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //The size of the Platform certificate (not in use)
        long platformSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx, TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //The size of the Conformance certificate (not in use)
        long conformanceSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx, TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //This part contains the data structure used by the TPM to store the AIK
        //We measure the whole AIK structure length to extract it later on
        int aikIdx=idx;
        //First algorithm parameters to use with the AIK
        //Algorithm ID
        long asymAlgorithmId=TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx,TPMUtils.UINT32)));
	idx=idx+TPMUtils.UINT32;
        //The encryption scheme        
        int asymEncScheme=TPMUtils.decodeUINT16(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx,TPMUtils.UINT16)));
        idx=idx+TPMUtils.UINT16;
        //The signature scheme        
        int asymSigScheme=TPMUtils.decodeUINT16(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx,TPMUtils.UINT16)));
        idx=idx+TPMUtils.UINT16;
        //The size of the field containing the algotrith parameteres
        long asymAlgParmSize = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx,TPMUtils.UINT32)));
        idx=idx+TPMUtils.UINT32;
        //The actual algorithm parameters
        byte[] asymAlgParams=TPMUtils.getRange(clearTextTPMReq, idx,(int) asymAlgParmSize);
        idx=idx+(int) asymAlgParmSize;
        //The size of the field containing the actual AIK
        long AIKlength = TPMUtils.decodeUINT32(TPMUtils.byteArrayToShortArray(TPMUtils.getRange(clearTextTPMReq, idx,TPMUtils.UINT32)));
        //We set it in the AIKRequest to be able to extract it again later
        req.setAIKSize((int) AIKlength);
        idx=idx+TPMUtils.UINT32;
        //The encoded AIK
        byte[] aik=TPMUtils.getRange(clearTextTPMReq, idx,(int) AIKlength);
        //We set it in the AIKRequest already parsed as a PublicKey object
        req.setAIK(TPMUtils.pubTpmKeyToJava(aik));
        //We store also the raw encoding 
        req.setAIKbytes(aik);
        idx=idx+(int) AIKlength;
        //Encoded AIK as tpm pubKey, ready for hashing. 
        //The modulus can be extracted by taking the last AIKlength bytes 
        //or reading directly the AIKBytes value.
        byte[] aikStruct=TPMUtils.getRange(clearTextTPMReq, aikIdx,idx-aikIdx);
        req.setAIKStruct(aikStruct);
        //Rest of the request
        //The label with the name of the PCA given as a parameter to the TPM for 
        //the AIK generation
        req.setPCALabel(TPMUtils.getRange(clearTextTPMReq, idx,(int) labelSize));
        idx=idx+(int) labelSize;
        //The signed hash consisting of (version||TPM_constant||SHA-1(PCALabel||PCAPubKey)||AIKbytes)
        req.setIdBinding(TPMUtils.getRange(clearTextTPMReq, idx,(int) identityBindingSize));
        idx=idx+(int) identityBindingSize;
        //The Endorsment certificate
        req.setEC(parseEC(TPMUtils.getRange(clearTextTPMReq, idx,(int) endorsementSize)));
        idx=idx+(int)endorsementSize;
        //Not really used
        byte[] platformCredential_ = TPMUtils.getRange(clearTextTPMReq, idx,(int) platformSize);
        idx=idx+(int) platformSize;
        byte[] conformanceCredential_ = TPMUtils.getRange(clearTextTPMReq, idx,(int) conformanceSize);
        
        byte[] tpmPCAPubKey=(TcCrypto.pubJavaToTpmKey(PCAPubKey)).getEncoded().asByteArray();
        req.setPCAPubKey(tpmPCAPubKey);
        int PCALength=PCAPubKey.getModulus().toByteArray().length;
        req.setPCALength(PCALength);
        req.setResponsePreamble(createResponseTemplate(aikStruct));
        
        
        
       return req;
    }
     /**
      * Parse the structure containing the signed PCR values produced with the 
      * "quote" TPM method. 
      * @param val
      * @return 
      */
     public static void parseQuote(ValidationData val, PCRInfo pcrs){
         byte[] nonce=val.getNonce();
         byte[] pcrComp=val.getData();
         byte[] sign=val.getValidationData();
         TcTpmQuoteInfo2 pcr=new TcTpmQuoteInfo2(TcBlobData.newByteArray(val.getData()));
         System.out.println(pcr.toString());
         byte[] digest = pcr.getInfoShort().getDigestAtRelease().getDigest().asByteArray();
        String pcrS = pcr.getInfoShort().getPcrSelection().toString();
        System.out.println("TcTpmCompositeHash digest ="+digest);
        System.out.println(pcrS);
     }
     
     /**
      * Creates the data structure that will be encrypted with the EK on the PKI
      * and become the asymmetricCABlob in the CA response. 
      * All TPM flags and parameters are in place, only the actual symmetric 
      * key needs to be filled in by the CA.
      * @param aikStruct
      * @return 
      */
     public static byte[] createResponseTemplate(byte[] aikStruct){
         byte[] template=new byte[8+16+20];
         TcTpmSymmetricKey symCaKey = new TcTpmSymmetricKey();
	 symCaKey.setAlgId(Constants.SYM_ALGO_TPM);
	 symCaKey.setEncScheme((int) TcTpmConstants.TPM_ES_SYM_CBC_PKCS5PAD);
         symCaKey.setData(TcBlobData.newByteArray(new byte[16]));
         byte[] symmKeyTempl= symCaKey.getEncoded().asByteArray();
         System.arraycopy(symmKeyTempl, 0, template, 0, symmKeyTempl.length);
         byte[] aikHash=TcBlobData.newByteArray(aikStruct).sha1().asByteArray();
         System.arraycopy(aikHash, 0, template, template.length-20, 20);
         return template;
     }
    /**
     * Create a X509Certificate from the encoded Endorsment certificate.
     * @param ecByte The encoded byte array containing the EC
     * @return The parsed EC
     */
    private static X509Certificate parseEC(byte[] ecByte) {
        X509Certificate EC=null;
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            InputStream in = new ByteArrayInputStream(ecByte);
            X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);
            EC=cert;
        } catch (CertificateException ex) {
            Logger.getLogger(AIKRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
         return EC;
    }
}
